﻿/*  Copyright 2012 PerceiveIT Limited
 *  This file is part of the Scryber library.
 *
 *  You can redistribute Scryber and/or modify 
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  Scryber is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Scryber source code in the COPYING.txt file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Scryber.Native;
using Scryber.Components;
using Scryber.Drawing;

namespace Scryber
{
    /// <summary>
    /// The document that wraps an original document and appends the PDFCollectorTraceLog and other info
    /// </summary>
    internal class PDFTraceLogDocument : Scryber.Components.PDFDocument
    {
        private PDFDocumentGenerationData _data;

        internal PDFDocumentGenerationData GenerationData
        {
            get { return _data; }
        }

        internal PDFTraceLogDocument(string name, PDFFile original, PDFDocumentGenerationData data)
            : base()
        {
            this.OriginalSourceFile = original;
            this.FileName = name;
            this._data = data;
            this.Info = data.DocumentInfo;
            this.ViewPreferences = data.DocumentViewerPrefs;
        }

        protected override void DoInit(PDFInitContext context)
        {
            context.TraceLog = new Scryber.Logging.DoNothingTraceLog(TraceRecordLevel.Off);
            base.DoInit(context);

            this.InitContent(context);
        }

        private void InitContent(PDFInitContext context)
        {
            PDFSection pg = new PDFSection() { PaperSize = Scryber.PaperSize.A4, FontSize = 10 };
            this.Pages.Add(pg);
            pg.FontSize = 10;

            PDFHead1 title = new PDFHead1() { Text = "Trace Output", BackgroundColor = new PDFColor(0.6), FullWidth = true, FillColor = PDFColors.White, Padding = new PDFThickness(5) };
            pg.Contents.Add(title);
            this.InitOverview(pg, context);
            this.InitPerformance(pg, _data.PerformanceMetrics);
            this.InitLogEntries(pg, _data.TraceLog, context);
        }

        private void InitOverview(PDFSection pg, PDFInitContext context)
        {
            PDFHead3 head = new PDFHead3() { Text = "Document Overview", BackgroundColor = new PDFColor(0.9), FullWidth=true, Padding = new PDFThickness(5) };
            pg.Contents.Add(head);

            PDFTableGrid tbl = new PDFTableGrid() { FullWidth = true, FontSize = 10, Margins = new PDFThickness(10) };
            pg.Contents.Add(tbl);

            if (null != this.Info && !string.IsNullOrEmpty(this.Info.Title))
                this.AddOverviewRow(tbl, "Title", this.Info.Title);
            
            if(null != this._data.DocumentID)
                this.AddOverviewRow(tbl, "Document ID", this._data.DocumentID.One + " " + this._data.DocumentID.Two);

            if (!string.IsNullOrEmpty(_data.TemplatePath))
                this.AddOverviewRow(tbl, "Source Template", this._data.TemplatePath);

            this.AddOverviewRow(tbl, "Scryber Version", this._data.ScryberVersion.ToString() + " (" + this._data.ScryberFileVersion + ")");
            this.AddOverviewRow(tbl, "Document Size", this.GetMbSizeString(this._data.DocumentSize));
            this.AddOverviewRow(tbl, "Generation Time ", this.GetGenTimeString(this._data.DocumentGenerationTime));
        }

        private void InitPerformance(PDFSection pg, PDFPerformanceMonitor metrics)
        {
            PDFHead3 head = new PDFHead3() { Text = "Performance Metrics", BackgroundColor = new PDFColor(0.9), FullWidth = true, Padding = new PDFThickness(5) };
            pg.Contents.Add(head);

            PDFTableGrid tbl = new PDFTableGrid() { FullWidth = true, FontSize = 10, Margins = new PDFThickness(10) };
            pg.Contents.Add(tbl);

            PDFTableHeaderRow top = new PDFTableHeaderRow();
            tbl.Rows.Add(top);

            PDFTableCell cell = new PDFTableHeaderCell() { FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral("Entry"));
            top.Cells.Add(cell);


            cell = new PDFTableHeaderCell() { HorizontalAlignment = Scryber.HorizontalAlignment.Right, Width = 100, FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral("Duration (ms)"));
            top.Cells.Add(cell);

            cell = new PDFTableHeaderCell() { HorizontalAlignment = Scryber.HorizontalAlignment.Right, Width = 60, FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral("Count"));
            top.Cells.Add(cell);

            foreach (PDFPerformanceMonitorEntry entry in metrics)
            {
                if(entry.MonitorCount > 0)
                    this.AddPerformanceEntry(tbl, entry); 
            }
        }

        private void AddPerformanceEntry(PDFTableGrid grid, PDFPerformanceMonitorEntry entry)
        {
            PDFTableRow row = new PDFTableRow();
            grid.Rows.Add(row);

            PDFTableCell cell = new PDFTableCell() { BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "PerfCategoryKey";
            cell.Contents.Add(new PDFTextLiteral(entry.MonitorKey));
            row.Cells.Add(cell);

            cell = new PDFTableCell() { HorizontalAlignment = Scryber.HorizontalAlignment.Right, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "PerfCategoryEntryRight";
            cell.Contents.Add(new PDFTextLiteral(entry.MonitorElapsed.TotalMilliseconds.ToString("#,##0.00")));
            row.Cells.Add(cell);

            cell = new PDFTableCell() { HorizontalAlignment = Scryber.HorizontalAlignment.Right, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "PerfCategoryEntryRight";
            cell.Contents.Add(new PDFTextLiteral(entry.MonitorCount.ToString()));
            row.Cells.Add(cell);

            if (entry.HasMeasurements)
            {
                foreach (PDFPerformanceMonitorMeasurement measure in entry.GetMeasurements())
                {
                    this.AddPerformanceMeasurement(grid, measure);
                }
            }
        }

        private void AddPerformanceMeasurement(PDFTableGrid grid, PDFPerformanceMonitorMeasurement measure)
        {
            PDFTableRow row = new PDFTableRow();
            grid.Rows.Add(row);


            PDFTableCell cell = new PDFTableCell() { BorderWidth = 1, BorderColor = PDFColors.Gray, FillColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "PerfCategoryMeasure";
            cell.Contents.Add(new PDFTextLiteral(measure.Key));
            row.Cells.Add(cell);

            cell = new PDFTableCell() { HorizontalAlignment = Scryber.HorizontalAlignment.Right, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "PerfCategoryEntryRight";
            cell.Contents.Add(new PDFTextLiteral(measure.Elapsed.TotalMilliseconds.ToString("#,##0.00")));
            row.Cells.Add(cell);

            cell = new PDFTableCell() { HorizontalAlignment = Scryber.HorizontalAlignment.Right, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "PerfCategoryEntryRight";
            row.Cells.Add(cell);
        }



        private string GetGenTimeString(TimeSpan ts)
        {
            StringBuilder sb = new StringBuilder();
            if (ts.TotalMinutes > 1)
            {
                int mins = (int)ts.TotalSeconds;
                sb.Append(mins.ToString());
                sb.Append("mins ");
                ts = ts.Subtract(new TimeSpan(0, mins, 0));
            }
            if (ts.Seconds > 0)
            {
                sb.Append(ts.Seconds.ToString());
                sb.Append("secs ");
                ts = ts.Subtract(new TimeSpan(0, 0, ts.Seconds));
            }
            sb.Append(ts.TotalMilliseconds.ToString("##0.000"));
            sb.Append("ms");

            return sb.ToString();
            
        }

        private string GetMbSizeString(long len)
        {
            const long Onekb = 1024;
            const long OneMb = 1024 * Onekb;

            StringBuilder sb = new StringBuilder();

            if (len > OneMb)
            {
                long mb = len / OneMb;
                len = len - (mb * OneMb);
                sb.Append(mb);
                sb.Append("Mb ");
            }
            if (len > Onekb)
            {
                long kb = len / Onekb;
                len = len - (kb * Onekb);
                sb.Append(kb);
                sb.Append("kb ");
            }
            
            sb.Append(len);

            return sb.ToString();
        }

        private void AddOverviewRow(PDFTableGrid tbl, string name, string value)
        {
            PDFTableRow row = new PDFTableRow();
            tbl.Rows.Add(row);

            PDFTableCell cell = new PDFTableHeaderCell() { Width=100, FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral(name));
            row.Cells.Add(cell);

            cell = new PDFTableCell() { BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            if(string.IsNullOrEmpty(value) == false)
                cell.Contents.Add(new PDFTextLiteral(value));
            row.Cells.Add(cell);


        }

        private void InitLogEntries(PDFSection pg, Logging.PDFCollectorTraceLog log, PDFInitContext context)
        {
            PDFHead3 head = new PDFHead3() { Text = "Document Log", FullWidth = true, BackgroundColor = new PDFColor(0.9), Padding = new PDFThickness(5) };
            pg.Contents.Add(head);

            PDFTableGrid tbl = new PDFTableGrid() { FullWidth = true, FontSize = 9, Margins = new PDFThickness(10) };
            pg.Contents.Add(tbl);

            PDFTableHeaderRow top = new PDFTableHeaderRow();
            tbl.Rows.Add(top);

            PDFTableCell cell = new PDFTableHeaderCell() { Width=60, FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral("Time (ms)"));
            top.Cells.Add(cell);

            cell = new PDFTableHeaderCell() { Width=60, FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral("Level"));
            top.Cells.Add(cell);

            cell = new PDFTableHeaderCell() { Width=100, FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral("Category"));
            top.Cells.Add(cell);

            cell = new PDFTableHeaderCell() { FontBold = true, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.Contents.Add(new PDFTextLiteral("Message"));
            top.Cells.Add(cell);

            foreach (Logging.PDFCollectorTraceLogEntry entry in log)
            {
                this.AddLogEntry(tbl, entry);
            }
        }

        private PDFColor messageBg = null;
        private PDFColor messageFill = PDFColors.Black;

        private PDFColor warningBg = new PDFColor(0.9, 0.9, 0.9);
        private PDFColor warningFill = PDFColors.Black;

        private PDFColor errorBg = PDFColors.Red;
        private PDFColor errorFill = PDFColors.White;

        private PDFColor verboseBg = null;
        private PDFColor verboseFill = PDFColors.Gray;


        private void AddLogEntry(PDFTableGrid tbl, Logging.PDFCollectorTraceLogEntry entry)
        {
            if (entry.Level == TraceLevel.Debug)
                return; //Skip the debug entries even if they were collected.

            PDFColor fill = PDFColors.Black;
            PDFColor bg = null;
            if (entry.HasException || entry.Level >= TraceLevel.Error)
            {
                bg = errorBg;
                fill = errorFill;
            }
            else if (entry.Level == TraceLevel.Warning)
            {
                bg = warningBg;
                fill = warningFill;
            }
            else if (entry.Level == TraceLevel.Message)
            {
                bg = messageBg;
                fill = messageFill;
            }
            else if (entry.Level <= TraceLevel.Verbose)
            {
                bg = verboseBg;
                fill = verboseFill;
            }

            string rowidentifier = "TraceRow" + entry.Level;


            PDFTableRow row = new PDFTableRow();
            if (null != bg)
                row.BackgroundColor = bg;
            if (null != fill)
                row.FillColor = fill;

            row.DataStyleIdentifier = rowidentifier;
            tbl.Rows.Add(row);

            PDFTableCell cell = new PDFTableHeaderCell() { FontBold = false, HorizontalAlignment = Scryber.HorizontalAlignment.Right, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "TraceCellStamp" + entry.Level;
            cell.Contents.Add(new PDFTextLiteral(entry.TimeStamp.TotalMilliseconds.ToString("#000.0000")));
            row.Cells.Add(cell);

            cell = new PDFTableHeaderCell() { FontBold = false, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "TraceCell" + entry.Level;
            cell.Contents.Add(new PDFTextLiteral(entry.Level.ToString()));
            row.Cells.Add(cell);

            cell = new PDFTableHeaderCell() { FontBold = false, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "TraceCell" + entry.Level;
            if(!string.IsNullOrEmpty(entry.Category))
                cell.Contents.Add(new PDFTextLiteral(entry.Category));
            row.Cells.Add(cell);

            cell = new PDFTableHeaderCell() { FontBold = false, BorderWidth = 1, BorderColor = PDFColors.Gray, Padding = new PDFThickness(2) };
            cell.DataStyleIdentifier = "TraceCell" + entry.Level;
            if (!string.IsNullOrEmpty(entry.Message))
                cell.Contents.Add(new PDFTextLiteral(entry.Message));

            if (entry.HasException)
            {
                cell.Contents.Add(new PDFLineBreak());
                cell.Contents.Add(new PDFTextLiteral(entry.Exception.ToString()));
            }
            row.Cells.Add(cell);

        }
    }
}
